package com.darknight.ftp.component.service.impl;

import com.darknight.ftp.component.service.UploadFileService;
import com.darknight.ftp.connection.FtpUserConnection;
import com.darknight.ftp.util.StringUtils;
import org.springframework.util.Assert;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.io.InputStream;

/**
 * Created by DarKnight on 2017/7/30.
 */
public abstract class AbstractUploadFileManager implements UploadFileService {
    /**
     * 传入的文件存储路径是否存在
     * @param storeFilePath 文件存储路径
     * @return
     */
    public boolean hasStoreFilePath(String storeFilePath) {
        return StringUtils.isNotBlank(storeFilePath);
    }

    /**
     * 通过FTP客户端登录FTP服务器
     * @param account
     * @param password
     * @param connection
     * @return
     * @throws IOException
     */
    @Override
    public boolean loginFtpServer(String account, String password, FtpUserConnection connection) throws IOException {
        Assert.notNull(account, "FTP登录账号为空");
        Assert.notNull(connection, "FTP连接为空");

        // 用户登录
        connection.loginFtpServer(account, password);

        return connection.isLoginSuccess();
    }

    /**
     * 退出指定FTP服务器连接的用户登录状态
     * @param connection FTP服务器连接
     * @return
     * @throws IOException
     */
    public boolean logout(FtpUserConnection connection) throws IOException {
        Assert.notNull(connection, "FTP连接为空");

        return connection.logout();
    }

    /**
     * 判断切换到传入的文件存储路径下是否成功
     * 将文件存储路径分割为若干父子级目录，并依次判断目录是否存在
     * 若目录存在则进入该目录，否则创建指定目录并进入
     * @param storeFilePath 文件存储路径
     * @param connection
     * @return
     * @throws IOException
     */
    public boolean reachStoreFilePath(String storeFilePath, FtpUserConnection connection) throws IOException {
        Assert.hasText(storeFilePath, "文件存储路径为空");
        Assert.notNull(connection, "FTP连接为空");

        return connection.reachStoreFilePath(storeFilePath);
    }

    /**
     *
     * @param storeFileName
     * @param inputStream
     * @param connection
     * @return
     * @throws IOException
     */
    public boolean storeFile(String storeFileName, InputStream inputStream, FtpUserConnection connection) throws IOException {
        Assert.hasText(storeFileName, "待上传文件名为空");
        Assert.notNull(inputStream, "上传文件输入流为空");
        Assert.notNull(connection, "FTP连接为空");

        return connection.storeFile(storeFileName, inputStream);
    }

    /**
     * 通过文件输入流上传目标文件至FTP服务器指定路径中，
     * 并命名为指定名称
     * @param ftpHostName
     * @param ftpPort
     * @param account
     * @param password
     * @param storeFileName 目标文件名称
     * @param inputStream 文件输入流
     * @param storeFilePath 文件存储路径
     */
    @Override
    public void uploadFile(String ftpHostName, Integer ftpPort, String account, String password,
                           String storeFileName, InputStream inputStream, String storeFilePath) {
        Assert.notNull(ftpHostName, "FTP访问路径为空");
        Assert.notNull(ftpPort, "FTP访问端口为空");
        Assert.notNull(account, "FTP登录账号为空");
        Assert.hasText(storeFileName, "待上传文件名为空");
        Assert.notNull(inputStream, "上传文件输入流为空");

        FtpUserConnection connection = null;
        // 获取已经联通的有效连接
        try {
            connection = getConnectedConnection(ftpHostName, ftpPort);

            // 通过账号和密码进行登录
            boolean loginSuccess = loginFtpServer(account, password, connection);
            // 登录失败时归还连接，返回操作标识并退出
            Assert.isTrue(loginSuccess, "FTP服务器登录失败");

            // 判断存放文件的目标文件夹路径是否存在
            if(hasStoreFilePath(storeFilePath)) {
                // 判断存放文件的目标文件夹路径是否可达
                boolean reachStoreFilePathResult = reachStoreFilePath(storeFilePath, connection);
                Assert.isTrue(reachStoreFilePathResult, "无法进入上传文件目录");
            }

            // 上传文件
            boolean storeSuccess = storeFile(storeFileName, inputStream, connection);
            Assert.isTrue(storeSuccess, "上传文件失败");

            // 退出登录
            boolean logoutSuccess = logout(connection);
            Assert.isTrue(logoutSuccess, "FTP服务器退出登录失败");
        }catch (IOException e) {
            e.printStackTrace();
            // 由于捕获了IO异常，并进行了相应处理
            // 将IO异常降级为Runtime异常，返回相应信息，以便进行后续提示和操作
            throw new RuntimeException(e.getMessage());
        }finally {
            // 归还连接
            returnConnection(connection);
        }
    }

    @Override
    public boolean uploadFile(String account, String password, MultipartFile uploadFile, String storeFilePath) {
        Assert.notNull(account, "FTP登录账号为空");
        Assert.notNull(uploadFile, "待上传文件为空");

        boolean optSuccess = false;
        InputStream inputStream = null;
        try {
            inputStream = uploadFile.getInputStream();
            String storeFileName = uploadFile.getOriginalFilename();
            uploadFile("localhost", 2121, account, password, storeFileName, inputStream, storeFilePath);
            optSuccess = true;
        }catch(IOException e) {
            e.printStackTrace();

            // 将IO异常降级为Runtime异常，返回相应信息，以便进行后续提示和操作
            throw new RuntimeException(e.getMessage());
        }finally {
            try {
                if(inputStream != null) {
                    inputStream.close();
                }
            }catch(IOException e) {
                e.printStackTrace();
                optSuccess = false;
                // 将IO异常降级为Runtime异常，返回相应信息，以便进行后续提示和操作
                throw new RuntimeException(e.getMessage());
            }finally {
                return optSuccess;
            }
        }
    }
}
